#include "stdafx.h"
#include <math.h>
#include "SceneManager.h"
#include "ResourceManager.h"
#include "Object.h"
#include "Shader.h"
#include "Texture.h"
#include "Model.h"
#include "Camera.h"
#include "KeyManager.h"

cObject::cObject():m_ID(-1), m_pShader(NULL), m_pModel(NULL), m_vTextures(),
		m_position(), m_rotate(), m_scale(), m_positionNext(), m_rotateNext(), m_scaleNext()
{
	m_maWorld.SetIdentity();
}

cObject::cObject(int id, cShader *pShader, cModel *pModel, std::vector<cTexture*> vTextures,
			Vector3 position, Vector3 rotate, Vector3 scale, Matrix& maWorld)
{
	cObject();
	m_ID = id;
	m_pShader = pShader;
	m_pModel = pModel;
	m_vTextures = vTextures;
	m_position = m_positionNext = position;
	m_rotate = m_rotateNext = rotate;
	m_scale = m_scaleNext = scale;
	m_maWorld = maWorld;
}

cObject::~cObject()
{
	m_vTextures.clear();
}

void cObject::Init()
{
	m_pShader->Init();
	m_pModel->Init();
	for (unsigned int i = 0; i < m_vTextures.size(); i++)
	{
		m_vTextures[i]->Init();
	}

	m_positionNext = m_position;
	m_rotateNext = m_rotate;
	m_scaleNext = m_scale;
}

void cObject::Update(float deltaTime)
{
	ComputeWorldMatrix(deltaTime);
}

void cObject::Render()
{
	GLuint program = m_pShader->m_program;
	int location = -1;
	int texIndex = 0;
	int vboIndex = 0;

	glUseProgram(program);

	glBindBuffer(GL_ARRAY_BUFFER, m_pModel->m_vboIds[0]);

	location = glGetAttribLocation(program, "a_positionL");
	if (location != -1)
	{
		glEnableVertexAttribArray(location);
		glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)vboIndex);
	}

	vboIndex += sizeof(Vector3);
	location = glGetAttribLocation(program, "a_normalL");
	if (location != -1)
	{
		glEnableVertexAttribArray(location);
		glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)vboIndex);
	}

	vboIndex += sizeof(Vector3);
	location = glGetAttribLocation(program, "a_bitangentL");
	if (location != -1)
	{
		glEnableVertexAttribArray(location);
		glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)vboIndex);
	}

	vboIndex += sizeof(Vector3);
	location = glGetAttribLocation(program, "a_tangentL");
	if (location != -1)
	{
		glEnableVertexAttribArray(location);
		glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)vboIndex);
	}

	vboIndex += sizeof(Vector3);
	location = glGetAttribLocation(program, "a_uv");
	if (location != -1)
	{
		glEnableVertexAttribArray(location);
		glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)vboIndex);
	}

	glBindBuffer(GL_ARRAY_BUFFER, 0);

	location = glGetUniformLocation(program, "u_wvpMatrix");
	if (location != -1)
	{
		Matrix mvpMatrix = m_maWorld * cCamera::GetInstance()->GetViewMatrix() * cCamera::GetInstance()->GetProjectionMatrix();

		glUniformMatrix4fv(location, 1, GL_FALSE, (GLfloat*)&mvpMatrix.m);
	}

	location = glGetUniformLocation(program, "u_wMatrix");
	if (location != -1)
	{
		glUniformMatrix4fv(location, 1, GL_FALSE, (GLfloat*)&m_maWorld.m);
	}

	location = glGetUniformLocation(program, "u_eyePosition");
	if (location != -1)
	{
		glUniform3fv(location, 1, &(cCamera::GetInstance()->m_vPos.x));
	}

	location = glGetUniformLocation(program, "u_sampler2D");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerCube");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_CUBE_MAP, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "samplerHeightMap");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerBlend");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerTerrain1");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerTerrain2");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerTerrain3");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_maxHeight");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_terrain.maxHeight);
	}

	location = glGetUniformLocation(program, "u_fogStart");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_terrain.fogStart);
	}

	location = glGetUniformLocation(program, "u_fogLength");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_terrain.fogLength);
	}

	location = glGetUniformLocation(program, "u_fogColor");
	if (location != -1)
	{
		glUniform4fv(location, 1, &cSceneManager::GetInstance()->m_terrain.fogColor.x);
	}

	location = glGetUniformLocation(program, "u_samplerDisMap");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerFire");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	location = glGetUniformLocation(program, "u_samplerFireMask");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	static float fireOffset = 0.0f;
	location = glGetUniformLocation(program, "u_fireOffset");
	if (location != -1)
	{
		if (fireOffset > 1.0f)
			fireOffset = 0.0f;
		fireOffset += 0.01f;
		glUniform1f(location, fireOffset);
	}

	location = glGetUniformLocation(program, "u_lightPosition");
	if (location != -1)
	{
		glUniform3fv(location, 1, &cSceneManager::GetInstance()->m_light.position.x);
	}

	location = glGetUniformLocation(program, "u_lightAmbient");
	if (location != -1)
	{
		glUniform4fv(location, 1, &cSceneManager::GetInstance()->m_light.ambient.x);
	}

	location = glGetUniformLocation(program, "u_lightDiffuse");
	if (location != -1)
	{
		glUniform4fv(location, 1, &cSceneManager::GetInstance()->m_light.diffuse.x);
	}

	location = glGetUniformLocation(program, "u_lightSpecular");
	if (location != -1)
	{
		glUniform4fv(location, 1, &cSceneManager::GetInstance()->m_light.specular.x);
	}

	location = glGetUniformLocation(program, "u_lightConstantAttenuation");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_light.constantAttenuation);
	}

	location = glGetUniformLocation(program, "u_lightLinearAttenuation");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_light.linearAttenuation);
	}

	location = glGetUniformLocation(program, "u_lightQuadraticAttenuation");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_light.quadraticAttenuation);
	}

	location = glGetUniformLocation(program, "u_lightShininess");
	if (location != -1)
	{
		glUniform1f(location, cSceneManager::GetInstance()->m_light.shininess);
	}

	location = glGetUniformLocation(program, "u_lightWeight");
	if (location != -1)
	{
		glUniform1f(location,  cSceneManager::GetInstance()->m_light.weight);
	}

	location = glGetUniformLocation(program, "u_samplerNormalMap");
	if (location != -1)
	{
		glActiveTexture(GL_TEXTURE0 + texIndex);
		glBindTexture(GL_TEXTURE_2D, m_vTextures[texIndex]->m_textureID);
		glUniform1i(location, texIndex);
		texIndex++;
	}

	if ((m_pShader->m_state & STATE_CULL) == STATE_CULL)
		glEnable(GL_CULL_FACE);
	if ((m_pShader->m_state & STATE_DEPTH) == STATE_DEPTH)
		glEnable(GL_DEPTH_TEST);
	if ((m_pShader->m_state & STATE_ALPHA) == STATE_ALPHA)
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pModel->m_vboIds[1]);
	glDrawElements(GL_TRIANGLES, m_pModel->m_iNumIndices, GL_UNSIGNED_INT, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	glDisable(GL_CULL_FACE);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);
}

void cObject::ComputeWorldMatrix(float deltaTime)
{
	Matrix maTranslate;
	Matrix maRotateX, maRotateY, maRotateZ;
	Matrix maScale;
	Vector3 vector;

	if (deltaTime > 0.1f)
	{
		deltaTime = 0.1f;
	}

	vector = m_positionNext - m_position;
	if (vector.Length() > 0.05)
	{
		m_position += (m_positionNext - m_position) * deltaTime;
	}
	else
	{
		m_position = m_positionNext;
	}

	vector = m_rotateNext - m_rotate;
	if (vector.Length() > 0.05)
	{
		m_rotate += (m_rotateNext - m_rotate) * deltaTime;
	}
	else
	{
		m_rotate = m_rotateNext;
	}

	vector = m_scaleNext - m_scale;
	if (vector.Length() > 0.05)
	{
		m_scale += (m_scaleNext - m_scale) * deltaTime;
	}
	else
	{
		m_scale = m_scaleNext;
	}

	maTranslate.SetTranslation(m_position);
	maRotateX.SetRotationX(RADIAN(m_rotate.x));
	maRotateY.SetRotationY(RADIAN(m_rotate.y));
	maRotateZ.SetRotationZ(RADIAN(m_rotate.z));
	maScale.SetScale(m_scale);

	m_maWorld = maScale * (maRotateZ * maRotateX * maRotateY) * maTranslate;
}

void cObject::SetTranslate(Vector3 &position)
{
	m_positionNext = position;
}

void cObject::SetRotate(Vector3 &rotate)
{
	m_rotateNext = rotate;
}

void cObject::SetScale(Vector3 &scale)
{
	m_scaleNext = scale;
}
